#include "SoundPotVstInstrument.h"



SoundPot::VstInstrument::VstInstrument()
{
}

void SoundPot::VstInstrument::setModulePath(const std::string& path)
{
	this->modulePath = path;
}

SoundPot::VstInstrument::~VstInstrument()
{
	close();
	unload();
}


void SoundPot::VstInstrument::load()
{
	load(staticPluginToHostCallback, modulePath);
	open();
}

void SoundPot::VstInstrument::unload()
{
	// Free library

	if(module)
	{
#if _WIN32
		FreeLibrary ((HMODULE) module);
#endif
	}
}

void SoundPot::VstInstrument::control(VstEvents* events)
{
	this->processEvents(events);

}

void SoundPot::VstInstrument::showEditor()
{
	theEffect = effect;

	MyDLGTEMPLATE t;	
	t.style = WS_POPUPWINDOW|WS_DLGFRAME|DS_MODALFRAME|DS_CENTER;
	t.cx = 100;
	t.cy = 100;
	DialogBoxIndirectParam (GetModuleHandle (0), &t, 0, (DLGPROC)EditorProc, (LPARAM)effect);

	theEffect = 0;
}


void SoundPot::VstInstrument::loadAssignedSound()
{
	// Todo: Set chunks, set parameters - if they are set
}


void SoundPot::VstInstrument::suspend()
{
	effect->dispatcher(effect, effMainsChanged, 0, 0, 0, 0);
}

void SoundPot::VstInstrument::wakeUp()
{
	effect->dispatcher(effect, effMainsChanged, 0, 1, 0, 0);
}

void SoundPot::VstInstrument::setSampleRate(const float& value)
{
	effect->dispatcher(effect, effSetSampleRate, 0, 0, 0, value);
}

void SoundPot::VstInstrument::setMaxBlockSize(const int& value)
{
	effect->dispatcher(effect, effSetBlockSize, 0, value, 0, 0);
}


void SoundPot::VstInstrument::load(TypeHostCallbackFunction pluginToHostCallback, const std::string& pathToPluginDll)
{
	// Load library
	// Todo: Exception, that library could not be loaded
#if _WIN32
	module = LoadLibraryA(pathToPluginDll.c_str());
#endif


	/*	Define a type, which represents the signature
		of the plugin's main() function, whose signature looks similar to this:

		AEffect* main(audioMasterCallback)

		audioMasterCallback is the function type, which represents the signature of the HostCallback function.
		
		This type is used to create a typed function pointer which in turn will point
		on the plugin's main function and will be used to get a reference to the AEffect
		object.
	*/

	// Todo: Exception
	

	typedef AEffect* (*TypePluginMainFunction) (TypeHostCallbackFunction);


	// Create a function pointer of this type

	TypePluginMainFunction mainFunction = NULL;




	// Get main function (there are two naming schemes, so try each for better compatibility)

#if _WIN32
	mainFunction = (TypePluginMainFunction) GetProcAddress((HMODULE) module, "VSTPluginMain");

	if(! mainFunction)
	{
		mainFunction = (TypePluginMainFunction) GetProcAddress((HMODULE) module, "main");
	}
#endif

	// Create effect instance by calling the plugin's main function which returns a reference to the new effect
	// Todo: Exception

	effect = mainFunction(pluginToHostCallback);
}



	


void SoundPot::VstInstrument::open()
{
	if(effect)
	{
		effect->dispatcher(effect, effOpen, 0, 0, 0, 0);
	}
}

void SoundPot::VstInstrument::close()
{
	if(effect)
	{
		effect->dispatcher(effect, effClose, 0, 0, 0, 0);
	}

}


void SoundPot::VstInstrument::fillNextAudioBlock(float** inputChannels, float** outputChannels, const VstInt32 blockSize)
{
	process(inputChannels, outputChannels, blockSize);
}


const std::string SoundPot::VstInstrument::getName()
{
	char value[256] = {NULL};

	
	if(effect)
	{	
		effect->dispatcher(effect, effGetEffectName, 0, 0, value, 0);
	}
	

	return value;
}

const std::string SoundPot::VstInstrument::getVendorString()
{
	char value[256] = {NULL};

	
	if(effect)
	{	
		effect->dispatcher(effect, effGetVendorString, 0, 0, value, 0);
	}
	

	return value;
}

const std::string SoundPot::VstInstrument::getProductString()
{
	char value[256] = {NULL};

	
	if(effect)
	{	
		effect->dispatcher(effect, effGetProductString, 0, 0, value, 0);
	}
	

	return value;
}

const VstInt32 SoundPot::VstInstrument::getNumberOfPrograms()
{
	return effect->numPrograms;
}

const VstInt32 SoundPot::VstInstrument::getNumberOfParameters()
{
	return effect->numParams;
}

const VstInt32 SoundPot::VstInstrument::getNumberOfInputs()
{
	return effect->numInputs;
}

const VstInt32 SoundPot::VstInstrument::getNumberOfOutputs()
{
	return effect->numOutputs;
}


const SoundPot::VstInstrument::Program SoundPot::VstInstrument::getProgramAt(const VstInt32 index)
{
	Program program;
	char name[256] = {NULL};

	/*	At first, try to get the program name by index automatically. If this was not successfull,
		try the old way (?) to switch to the program to the specified index before getting the program name.
		After this restore the old program. Hopefully the first try is successfull!
	*/
	if(!effect->dispatcher(effect, effGetProgramNameIndexed, index, 0, name, 0))
	{
		// Todo: Restore oldProgram
		// VstInt32 oldIndex = 
		effect->dispatcher(effect, effSetProgram, 0, index, 0, 0);
		effect->dispatcher(effect, effGetProgramName, 0, 0, name, 0);
		// effect->dispatcher(effect, effSetProgram, 0, oldIndex, 0, 0);
	}

	program.Name = name;
	return program;
}

const SoundPot::VstInstrument::Parameter SoundPot::VstInstrument::getParameterAt(const VstInt32 index)
{
	Parameter parameter;

	char parameterName[256] = {NULL};
	char parameterLabel[256] = {NULL};
	char parameterDisplay[256] = {NULL};

	effect->dispatcher(effect, effGetParamName, index, 0, parameterName, 0);
	effect->dispatcher(effect, effGetParamLabel, index, 0, parameterLabel, 0);
	effect->dispatcher(effect, effGetParamDisplay, index, 0, parameterDisplay, 0);

	parameter.Value = effect->getParameter(effect, index);
	parameter.Name = parameterName;
	parameter.Label = parameterLabel;
	parameter.Display = parameterDisplay;
	return parameter;
}

const SoundPot::VstInstrument::CanDoAnswer SoundPot::VstInstrument::canDo(const std::string& feature)
{
	VstInt32 result = (VstInt32) effect->dispatcher(effect, effCanDo, 0, 0, (void*) feature.c_str(), 0);
	
	switch(result)
	{
	case 0:
		return unsure;
	case 1:
		return yes;
	case -1:
		return no;
	default: // Todo: Raise some exception
		return unsure;
	}
}

const bool SoundPot::VstInstrument::hasEditor()
{
	return effect->flags & effFlagsHasEditor;
}

void SoundPot::VstInstrument::process(float** inputChannels, float** outputChannels, const VstInt32 numSampleFrames)
{
	// Todo: Somehow include the old processAccumulating (is this necessary for VST instruments?)
	effect->processReplacing(effect, inputChannels, outputChannels, numSampleFrames);
}

void SoundPot::VstInstrument::processEvents(VstEvents* events)
{
	effect->dispatcher(effect, effProcessEvents, 0, 0, events, 0);
}






INT_PTR CALLBACK SoundPot::VstInstrument::EditorProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	AEffect* effect = theEffect;

	switch(msg)
	{
		//-----------------------
		case WM_INITDIALOG :
		{
			SetWindowText (hwnd, L"VST Editor");
			SetTimer (hwnd, 1, 20, 0);

			if (effect)
			{
				// Open editor
				effect->dispatcher (effect, effEditOpen, 0, 0, hwnd, 0);

				// Get editor rect
				ERect* eRect = 0;
				effect->dispatcher (effect, effEditGetRect, 0, 0, &eRect, 0);
				if (eRect)
				{
					int width = eRect->right - eRect->left;
					int height = eRect->bottom - eRect->top;
					if (width < 100)
						width = 100;
					if (height < 100)
						height = 100;

					RECT wRect;
					SetRect (&wRect, 0, 0, width, height);
					AdjustWindowRectEx (&wRect, GetWindowLong (hwnd, GWL_STYLE), FALSE, GetWindowLong (hwnd, GWL_EXSTYLE));
					width = wRect.right - wRect.left;
					height = wRect.bottom - wRect.top;

					SetWindowPos (hwnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE);
				}
			}
		}	break;

		//-----------------------
		case WM_TIMER :
			if (effect)
				effect->dispatcher (effect, effEditIdle, 0, 0, 0, 0);
			break;

		//-----------------------
		case WM_CLOSE :
		{
			KillTimer (hwnd, 1);

			// Close editor
			if (effect)
				effect->dispatcher (effect, effEditClose, 0, 0, 0, 0);

			EndDialog (hwnd, IDOK);
		}	break;
	}

	return 0;
}


VstIntPtr VSTCALLBACK SoundPot::VstInstrument::pluginToHostCallback (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
	VstIntPtr result = 0;


	// Filter idle calls...
	bool filtered = false;
	if (opcode == audioMasterIdle)
	{
		static bool wasIdle = false;
		if (wasIdle)
			filtered = true;
		else
		{
			wasIdle = true;
		}
	}


	switch (opcode)
	{
		case audioMasterVersion :
			result = kVstVersion;
			break;
	}

	return result;
}

VstIntPtr VSTCALLBACK SoundPot::VstInstrument::staticPluginToHostCallback (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
	// Todo: Forward to object method by using a map of instruments identified by the AEffect
	VstIntPtr result = 0;


	// Filter idle calls...
	bool filtered = false;
	if (opcode == audioMasterIdle)
	{
		static bool wasIdle = false;
		if (wasIdle)
			filtered = true;
		else
		{
			wasIdle = true;
		}
	}


	switch (opcode)
	{
		case audioMasterVersion :
			result = kVstVersion;
			break;
	}

	return result;
	// return SoundPot::VstInstrumentFactory::getInstance()->pluginToHostCallback(effect, opcode, index, value, ptr, opt);
}